using System;
using System.IO;
using System.Globalization;
using System.Collections;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Security.Permissions;
#if !PocketPC || DesignTime
using System.Runtime.Serialization;
using System.ComponentModel;
#endif
using System.Reflection;

namespace GeoFramework
{

	/// <summary>Represents a flattened sphere which approximates Earth's size and shape.</summary>
	/// <remarks>
	/// 	<para>Mathematics involving points on Earth's surface are difficult to perform with
	///     precision because the Earth's surface is rugged. In order to maximize precision,
	///     scientists developed "ellipsoids," smooth ellipsoidal shapes (known as "oblate
	///     spheriods" or flattened spheres) which attempt to approximate Earth's exact shape.
	///     Like datums, ellipsoids have been subject to frequent revisions thanks to advances
	///     in technology, yet countries cannot quickly abandon outdated ellipsoids because so
	///     much infrastructure is built upon them. As a result, multiple ellipsoids are
	///     tracked and utilized when converting coordinates from one locale to another. Today,
	///     there are approximately thirty known ellipsoids upon which an estimated 120
	///     individual coordinate systems are built.</para>
	/// 	<para>This class is typically used during coordinate conversion to convert from one
	///     interpretation of Earth's shape to another. All known worldwide ellipsoids such as
	///     WGS84 and Clarke 1880 are provided as static (Shared in Visual Basic) fields. Most
	///     developers will not have to use this class until coordinates must be plotted on a
	///     map. For most purposes, using the default ellipsoid of WGS84 is sufficient.</para>
	/// 	<para>Instances of this class are guaranteed to be thread-safe because the class is
	///     immutable (its properties can only be set via constructors).</para>
	/// </remarks>
#if !PocketPC || DesignTime
#if Framework20
    [Obfuscation(Feature = "renaming", Exclude = false, ApplyToMembers = true)]
    [Obfuscation(Feature = "controlflow", Exclude = true, ApplyToMembers = true)]
    [Obfuscation(Feature = "stringencryption", Exclude = false, ApplyToMembers = true)]
#endif
    [Serializable()]
	[TypeConverter(typeof(ExpandableObjectConverter))]
    [ImmutableObject(true)]
#endif
#if Framework20
    [XmlSchemaProvider("GetXmlSchema")]
#endif
    public sealed class Ellipsoid : IFormattable
#if !PocketPC || (PocketPC && Framework20)
		, IXmlSerializable
#endif 
#if !PocketPC  || DesignTime
       , ISerializable
#endif
    {
        private static ArrayList pEllipsoids = new ArrayList(32);

        private Distance pEquatorialRadius;
		private Distance pPolarRadius;
		private string pName;
		private double pFlattening;
		private double pInverseFlattening;
		private double pEccentricity;
		private double pEccentricitySquared;

		//private const int DefaultPrecisionDigits = 10;

        /// <summary>
        /// Creates a new instance with empty values.
        /// </summary>
        public Ellipsoid()
            :this("Empty", Distance.Empty, Distance.Empty)
        {}

		/// <summary>
		/// Creates a new instance with the specified type, name, equatorial raduis and polar radius.
		/// </summary>
		/// <param name="name">The name for the new ellipsoid.</param>
		/// <param name="equatorialRadius">A <strong>Distance</strong> object specifying the distance from the center of the Earth to the equator.</param>
		/// <param name="polarRadius">A <strong>Distance</strong> object specifying the distance from the center of the Earth to the poles.</param>
		/// <remarks>This constructor allows user-defined ellipsoids to be created for specialized applications.  </remarks>
		//[CLSCompliant(false)]
		public Ellipsoid(string name, Distance equatorialRadius, Distance polarRadius)
		{
			pName = name;
			pEquatorialRadius = equatorialRadius;
			pPolarRadius = polarRadius;
			// Perform calculations
            Calculate();

            // And add it to the list
            pEllipsoids.Add(this);
		}

#if !PocketPC || DesignTime
        /// <summary>
        /// Creates a new instance by de-serializing previously-serialized information.
        /// </summary>
        /// <param name="info">A <strong>SerializationInfo</strong> object containing serialized data.</param>
        /// <param name="context">A <strong>StreamingContext</strong> object describing the context of serialization.</param>
        /// <remarks>This constructor will de-serialize a previously-serialized object.  Objects are created
        /// serialized and de-serialized based on the "DecimalDegrees" value to maintain precision.</remarks>
        private Ellipsoid(SerializationInfo info, StreamingContext context)
        {
            pName = info.GetString("Name");
            pEquatorialRadius = (Distance)info.GetValue("EquatorialRadius", typeof(Distance));
            pPolarRadius = (Distance)info.GetValue("PolarRadius", typeof(Distance));

            // Perform calculations
            Calculate();

            // And add it to the list
            pEllipsoids.Add(this);
        }
#endif

        private void Calculate()
        {
            pFlattening = (pEquatorialRadius.ToMeters().Value - pPolarRadius.ToMeters().Value) / pEquatorialRadius.ToMeters().Value;
            pInverseFlattening = 1.0 / pFlattening;
            pEccentricity = Math.Sqrt((Math.Pow(pEquatorialRadius.Value, 2) - Math.Pow(pPolarRadius.Value, 2)) / Math.Pow(pEquatorialRadius.Value, 2));
            pEccentricitySquared = Math.Pow(Eccentricity, 2);
        }

		#region Static Variables

        /// <summary>
        /// Returns the entire collection of known ellipsoids.
        /// </summary>
        public static ArrayList Ellipsoids
        {
            get
            {
                return pEllipsoids;
            }
        }

		/// <summary>Represents the Clarke 1866 ellipsoid.</summary>
		public static readonly Ellipsoid Clarke1866 = new Ellipsoid("Clarke 1866", new Distance(6378206.4, DistanceUnit.Meters), new Distance(6356583.8, DistanceUnit.Meters));
		/// <summary>Represents the Clarke 1880 ellipsoid.</summary>
		public static readonly Ellipsoid Clarke1880 = new Ellipsoid("Clarke 1880", new Distance(6378249.145, DistanceUnit.Meters), new Distance(6356514.86955, DistanceUnit.Meters));
		/// <summary>Represents the Bessel 1841 ellipsoid.</summary>
		public static readonly Ellipsoid Bessel1841 = new Ellipsoid("Bessel 1841", new Distance(6377397.155, DistanceUnit.Meters), new Distance(6356078.96284, DistanceUnit.Meters));
		/// <summary>Represents the New International ellipsoid of 1967.</summary>
		public static readonly Ellipsoid NewInternational1967 = new Ellipsoid("New International 1967", new Distance(6378157.5, DistanceUnit.Meters), new Distance(6356772.2, DistanceUnit.Meters));
		/// <summary>Represents the International ellipsoid of 1924.</summary>
		public static readonly Ellipsoid International1924 = new Ellipsoid("International 1924", new Distance(6378388.0, DistanceUnit.Meters), new Distance(6356911.94613, DistanceUnit.Meters));
		/// <summary>Represents the World Geodetic System ellipsoid of 1972.</summary>
		public static readonly Ellipsoid Wgs1972 = new Ellipsoid("WGS 1972", new Distance(6378135.0, DistanceUnit.Meters), new Distance(6356750.519915, DistanceUnit.Meters));
		/// <summary>Represents the Everest (India 1830) ellipsoid.</summary>
		public static readonly Ellipsoid EverestIndia1830 = new Ellipsoid("Everest (India 1830)", new Distance(6377276.3452, DistanceUnit.Meters), new Distance(6356075.4133, DistanceUnit.Meters));
		/// <summary>Represents the World Geodetic System ellipsoid of 1966.</summary>
		public static readonly Ellipsoid Wgs1966 = new Ellipsoid("WGS 1966", new Distance(6378145.0, DistanceUnit.Meters), new Distance(6356759.769356, DistanceUnit.Meters));
		/// <summary>Represents the Geodetic Reference System ellipsoid of 1980.</summary>
		public static readonly Ellipsoid Grs80 = new Ellipsoid("GRS 1980", new Distance(6378137.0, DistanceUnit.Meters), new Distance(6356752.31414, DistanceUnit.Meters));
		/// <summary>Represents the Airy ellipsoid of 1830.</summary>
		public static readonly Ellipsoid Airy1830 = new Ellipsoid("Airy 1830", new Distance(6377563.396, DistanceUnit.Meters), new Distance(6356256.91, DistanceUnit.Meters));
		/// <summary>Represents the Everest ellipsoid of 1948.</summary>
		public static readonly Ellipsoid Everest1948 = new Ellipsoid("Everest 1948", new Distance(6377304.063, DistanceUnit.Meters), new Distance(6356103.039, DistanceUnit.Meters));
		/// <summary>Represents the Modified Airy ellipsoid.</summary>
		public static readonly Ellipsoid ModifiedAiry = new Ellipsoid("Modified Airy", new Distance(6377340.189, DistanceUnit.Meters), new Distance(6356034.448, DistanceUnit.Meters));
		/// <summary>Represents the World Geodetic System ellipsoid of 1984.</summary>
		public static readonly Ellipsoid Wgs1984 = new Ellipsoid("WGS 1984", new Distance(6378137.0, DistanceUnit.Meters), new Distance(6356752.314245, DistanceUnit.Meters));
		/// <summary>Represents the Modified Fisher ellipsoid of 1960.</summary>
		public static readonly Ellipsoid ModifiedFischer1960 = new Ellipsoid("Modified Fisher 1960", new Distance(6378155.0, DistanceUnit.Meters), new Distance(6356773.3205, DistanceUnit.Meters));
		/// <summary>Represents the Australian National ellipsoid of 1965.</summary>
		public static readonly Ellipsoid AustralianNational1965 = new Ellipsoid("Australian National 1965", new Distance(6378160.0, DistanceUnit.Meters), new Distance(6356774.719, DistanceUnit.Meters));
		/// <summary>Represents the Krassovsky ellipsoid of 1940.</summary>
		public static readonly Ellipsoid Krassovsky1940 = new Ellipsoid("Krassovsky 1940", new Distance(6378245.0, DistanceUnit.Meters), new Distance(6356863.0188, DistanceUnit.Meters));
		/// <summary>Represents the Hough ellipsoid of 1960.</summary>
		public static readonly Ellipsoid Hough1960 = new Ellipsoid("Hough 1960", new Distance(6378270.0, DistanceUnit.Meters), new Distance(6356794.343479, DistanceUnit.Meters));
		/// <summary>Represents the Fisher ellipsoid of 1960.</summary>
		public static readonly Ellipsoid Fischer1960 = new Ellipsoid("Fisher 1960", new Distance(6378166.0, DistanceUnit.Meters), new Distance(6356784.283666, DistanceUnit.Meters));
		/// <summary>Represents the Fisher ellipsoid of 1968.</summary>
		public static readonly Ellipsoid Fischer1968 = new Ellipsoid("Fisher 1968", new Distance(6378150.0, DistanceUnit.Meters), new Distance(6356768.337303, DistanceUnit.Meters));
		/// <summary>Represents a perfect sphere.</summary>
		public static readonly Ellipsoid NormalSphere = new Ellipsoid("Normal Sphere", new Distance(6370997.0, DistanceUnit.Meters), new Distance(6370997.0, DistanceUnit.Meters));
		/// <summary>Represents the Indonesian ellipsoid of 1974.</summary>
		public static readonly Ellipsoid Indonesian1974 = new Ellipsoid("Indonesian 1974", new Distance(6378160.0, DistanceUnit.Meters), new Distance(6356774.504086, DistanceUnit.Meters));
		/// <summary>Represents the Everest (Pakistan) ellipsoid.</summary>
		public static readonly Ellipsoid EverestPakistan = new Ellipsoid("Everest (Pakistan)", new Distance(6377309.613, DistanceUnit.Meters), new Distance(6356108.570542, DistanceUnit.Meters));
		/// <summary>Represents the Bessel (Japan) ellipsoid of 1841.</summary>
		public static readonly Ellipsoid Bessel1841Japan = new Ellipsoid("Bessel 1841 (Japan)", new Distance(6377397.155, DistanceUnit.Meters), new Distance(6356078.963, DistanceUnit.Meters));
		/// <summary>Represents the Bessel (Namibia) ellipsoid of 1841.</summary>
		public static readonly Ellipsoid Bessel1841Namibia = new Ellipsoid("Bessel 1841 (Namibia)", new Distance(6377483.865, DistanceUnit.Meters), new Distance(6356165.382966, DistanceUnit.Meters));
		/// <summary>Represents the Everest ellipsoid of 1956.</summary>
		public static readonly Ellipsoid Everest1956 = new Ellipsoid("Everest 1956", new Distance(6377301.243, DistanceUnit.Meters), new Distance(6356100.228368, DistanceUnit.Meters));
		/// <summary>Represents the Everest ellipsoid of 1969.</summary>
		public static readonly Ellipsoid Everest1969 = new Ellipsoid("Everest 1969", new Distance(6377295.664, DistanceUnit.Meters), new Distance(6356094.667915, DistanceUnit.Meters));
		/// <summary>Represents the Everest ellipsoid.</summary>
		public static readonly Ellipsoid Everest = new Ellipsoid("Everest", new Distance(6377298.556, DistanceUnit.Meters), new Distance(6356097.550301, DistanceUnit.Meters));
		/// <summary>Represents the Helmert ellipsoid of 1906.</summary>
		public static readonly Ellipsoid Helmert1906 = new Ellipsoid("Helmert 1906", new Distance(6378200.0, DistanceUnit.Meters), new Distance(6356818.169628, DistanceUnit.Meters));
		/// <summary>Represents the SGS ellipsoid of 1985.</summary>
		public static readonly Ellipsoid Sgs1985 = new Ellipsoid("SGS 85", new Distance(6378136.0, DistanceUnit.Meters), new Distance(6356751.301569, DistanceUnit.Meters));
		/// <summary>Represents the World Geodetic System ellipsoid of 1960.</summary>
		public static readonly Ellipsoid Wgs1960 = new Ellipsoid("WGS 60", new Distance(6378165.0, DistanceUnit.Meters), new Distance(6356783.286959, DistanceUnit.Meters));
		/// <summary>Represents the South American ellipsoid of 1969.</summary>
		public static readonly Ellipsoid SouthAmerican1969 = new Ellipsoid("South American 1969", new Distance(6378160.0, DistanceUnit.Meters), new Distance(6356774.719, DistanceUnit.Meters));
		/// <summary>Represents the Average Terrestrial System ellipsoid of 1977.</summary>
		public static readonly Ellipsoid Ats1977 = new Ellipsoid("ATS77", new Distance(6378135.0, DistanceUnit.Meters), new Distance(6356750.304922, DistanceUnit.Meters));
		/// <summary>Represents the default ellipsoid, WGS1984.</summary>
		public static readonly Ellipsoid Default = Ellipsoid.Wgs1984;

		#region Old Definitions
		
//		public static readonly Ellipsoid Clarke1866 = new Ellipsoid(EllipsoidType.Clarke1866, "Clarke 1866", new Distance(6378206.4, DistanceUnit.Meters), new Distance(6356583.8, DistanceUnit.Meters));
//		public static readonly Ellipsoid Clarke1880 = new Ellipsoid(EllipsoidType.Clarke1880, "Clarke 1880", new Distance(6378249.145, DistanceUnit.Meters), new Distance(6356514.86955, DistanceUnit.Meters));
//		public static readonly Ellipsoid Bessel1841 = new Ellipsoid(EllipsoidType.Bessel1841, "Bessel 1841", new Distance(6377397.155, DistanceUnit.Meters), new Distance(6356078.96284, DistanceUnit.Meters));
//		public static readonly Ellipsoid NewInternational1967 = new Ellipsoid(EllipsoidType.NewInternational1967, "New International 1967", new Distance(6378157.5, DistanceUnit.Meters), new Distance(6356772.2, DistanceUnit.Meters));
//		public static readonly Ellipsoid International1924 = new Ellipsoid(EllipsoidType.International1924, "International 1924", new Distance(6378388.0, DistanceUnit.Meters), new Distance(6356911.94613, DistanceUnit.Meters));
//		public static readonly Ellipsoid Wgs1972 = new Ellipsoid(EllipsoidType.Wgs1972, "WGS 1972", new Distance(6378135.0, DistanceUnit.Meters), new Distance(6356750.519915, DistanceUnit.Meters));
//		public static readonly Ellipsoid EverestIndia1830 = new Ellipsoid(EllipsoidType.EverestIndia1830, "Everest (India 1830)", new Distance(6377276.3452, DistanceUnit.Meters), new Distance(6356075.4133, DistanceUnit.Meters));
//		public static readonly Ellipsoid Wgs1966 = new Ellipsoid(EllipsoidType.Wgs1966, "WGS 1966", new Distance(6378145.0, DistanceUnit.Meters), new Distance(6356759.769356, DistanceUnit.Meters));
//		public static readonly Ellipsoid Grs80 = new Ellipsoid(EllipsoidType.Grs80, "GRS 1980", new Distance(6378137.0, DistanceUnit.Meters), new Distance(6356752.31414, DistanceUnit.Meters));
//		public static readonly Ellipsoid Airy1830 = new Ellipsoid(EllipsoidType.Airy1830, "Airy 1830", new Distance(6377563.396, DistanceUnit.Meters), new Distance(6356256.91, DistanceUnit.Meters));
//		public static readonly Ellipsoid Everest1948 = new Ellipsoid(EllipsoidType.Everest1948, "Everest 1948", new Distance(6377304.063, DistanceUnit.Meters), new Distance(6356103.039, DistanceUnit.Meters));
//		public static readonly Ellipsoid ModifiedAiry = new Ellipsoid(EllipsoidType.ModifiedAiry, "Modified Airy", new Distance(6377340.189, DistanceUnit.Meters), new Distance(6356034.448, DistanceUnit.Meters));
//		public static readonly Ellipsoid Wgs1984 = new Ellipsoid(EllipsoidType.Wgs1984, "WGS 1984", new Distance(6378137.0, DistanceUnit.Meters), new Distance(6356752.314245, DistanceUnit.Meters));
//		public static readonly Ellipsoid ModifiedFischer1960 = new Ellipsoid(EllipsoidType.ModifiedFischer1960, "Modified Fisher 1960", new Distance(6378155.0, DistanceUnit.Meters), new Distance(6356773.3205, DistanceUnit.Meters));
//		public static readonly Ellipsoid AustralianNational1965 = new Ellipsoid(EllipsoidType.AustralianNational1965, "Australian National 1965", new Distance(6378160.0, DistanceUnit.Meters), new Distance(6356774.719, DistanceUnit.Meters));
//		public static readonly Ellipsoid Krassovsky1940 = new Ellipsoid(EllipsoidType.Krassovsky1940, "Krassovsky 1940", new Distance(6378245.0, DistanceUnit.Meters), new Distance(6356863.0188, DistanceUnit.Meters));
//		public static readonly Ellipsoid Hough1960 = new Ellipsoid(EllipsoidType.Hough1960, "Hough 1960", new Distance(6378270.0, DistanceUnit.Meters), new Distance(6356794.343479, DistanceUnit.Meters));
//		public static readonly Ellipsoid Fischer1960 = new Ellipsoid(EllipsoidType.Fischer1960, "Fisher 1960", new Distance(6378166.0, DistanceUnit.Meters), new Distance(6356784.283666, DistanceUnit.Meters));
//		public static readonly Ellipsoid Fischer1968 = new Ellipsoid(EllipsoidType.Fischer1968, "Fisher 1968", new Distance(6378150.0, DistanceUnit.Meters), new Distance(6356768.337303, DistanceUnit.Meters));
//		public static readonly Ellipsoid NormalSphere = new Ellipsoid(EllipsoidType.NormalSphere, "Normal Sphere", new Distance(6370997.0, DistanceUnit.Meters), new Distance(6370997.0, DistanceUnit.Meters));
//		public static readonly Ellipsoid Indonesian1974 = new Ellipsoid(EllipsoidType.Indonesian1974, "Indonesian 1974", new Distance(6378160.0, DistanceUnit.Meters), new Distance(6356774.504086, DistanceUnit.Meters));
//		public static readonly Ellipsoid EverestPakistan = new Ellipsoid(EllipsoidType.EverestPakistan, "Everest (Pakistan)", new Distance(6377309.613, DistanceUnit.Meters), new Distance(6356108.570542, DistanceUnit.Meters));
//		public static readonly Ellipsoid Bessel1841Japan = new Ellipsoid(EllipsoidType.Bessel1841Japan, "Bessel 1841 (Japan)", new Distance(6377397.155, DistanceUnit.Meters), new Distance(6356078.963, DistanceUnit.Meters));
//		public static readonly Ellipsoid Bessel1841Namibia = new Ellipsoid(EllipsoidType.Bessel1841Namibia, "Bessel 1841 (Namibia)", new Distance(6377483.865, DistanceUnit.Meters), new Distance(6356165.382966, DistanceUnit.Meters));
//		public static readonly Ellipsoid Everest1956 = new Ellipsoid(EllipsoidType.Everest1956, "Everest 1956", new Distance(6377301.243, DistanceUnit.Meters), new Distance(6356100.228368, DistanceUnit.Meters));
//		public static readonly Ellipsoid Everest1969 = new Ellipsoid(EllipsoidType.Everest1969, "Everest 1969", new Distance(6377295.664, DistanceUnit.Meters), new Distance(6356094.667915, DistanceUnit.Meters));
//		public static readonly Ellipsoid Everest = new Ellipsoid(EllipsoidType.Everest, "Everest", new Distance(6377298.556, DistanceUnit.Meters), new Distance(6356097.550301, DistanceUnit.Meters));
//		public static readonly Ellipsoid Helmert1906 = new Ellipsoid(EllipsoidType.Helmert1906, "Helmert 1906", new Distance(6378200.0, DistanceUnit.Meters), new Distance(6356818.169628, DistanceUnit.Meters));
//		public static readonly Ellipsoid Sgs1985 = new Ellipsoid(EllipsoidType.Sgs1985, "SGS 85", new Distance(6378136.0, DistanceUnit.Meters), new Distance(6356751.301569, DistanceUnit.Meters));
//		public static readonly Ellipsoid Wgs1960 = new Ellipsoid(EllipsoidType.Wgs1960, "WGS 60", new Distance(6378165.0, DistanceUnit.Meters), new Distance(6356783.286959, DistanceUnit.Meters));
//		public static readonly Ellipsoid SouthAmerican1969 = new Ellipsoid(EllipsoidType.SouthAmerican1969, "South American 1969", new Distance(6378160.0, DistanceUnit.Meters), new Distance(6356774.719, DistanceUnit.Meters));
//		public static readonly Ellipsoid Ats1977 = new Ellipsoid(EllipsoidType.Ats1977, "ATS77", new Distance(6378135.0, DistanceUnit.Meters), new Distance(6356750.304922, DistanceUnit.Meters));
//
//		public static readonly Ellipsoid Default = Ellipsoid.Wgs1984;

		#endregion
		#endregion

		public override int GetHashCode()
		{
			return pEquatorialRadius.GetHashCode() ^ pPolarRadius.GetHashCode();
		}


		/// <summary>
		/// Indicates the descriptive name of the ellipsoid.
		/// </summary>
		/// <value>A <strong>String</strong> containing the name of the ellipsoid.</value>
		/// <remarks>This property is typically used to display ellipsoid information on a user interface.</remarks>
		//[XmlAttribute()]
		public string Name
		{
			get
			{
				return pName;
			}
		}

        ///// <summary>
        ///// Returns the total surface area of the ellipsoid
        ///// </summary>
        ///// <remarks>This method calculates the total area of the ellipsoid.  This method is typically used
        ///// to calculate the total approximate area of Earth's surface (without including terrain).</remarks>
        //public Area Area
        //{
        //    get
        //    {
        //        // 4/3 * Pi * PolarRadius * PolarRadius * EquatorialRadius
        //        return new Area((4 / 3) * Math.PI * Math.Pow(pEquatorialRadius.ToMeters().Value, 2) * pPolarRadius.ToMeters().Value, AreaUnit.SquareMeters).ToLocalUnitType();
        //    }
        //}

		/// <summary>Represents the distance from Earth's center to the equator.</summary>
		/// <value>A <strong>Distance</strong> object.</value>
		/// <remarks>
		/// 	<para>This property defines the radius of the Earth from its center to the equator.
		///     This property is used in conjunction with the <strong>PolarRadius</strong> property
		///     to define an ellipsoidal shape. This property returns the same value as the
		///     <strong>SemiMajorAxis</strong> property.</para>
		/// </remarks>
		/// <seealso cref="PolarRadius">PolarRadius Property</seealso>
		//[XmlElement()]
		//[CLSCompliant(false)]
		public Distance EquatorialRadius
		{
			get
			{
				return pEquatorialRadius;
			}
		}

		/// <summary>Represents the distance from Earth's center to the North or South pole.</summary>
		/// <value>A <strong>Distance</strong> object.</value>
		/// <remarks>
		/// 	<para>This property defines the radius of the Earth from its center to the equator.
		///     This property is used in conjunction with the <strong>EquatorialRadius</strong>
		///     property to define an ellipsoidal shape. This property returns the same value as
		///     the <strong>SemiMinorAxis</strong> property.</para>
		/// </remarks>
		/// <seealso cref="EquatorialRadius">EquatorialRadius Property</seealso>
		//[XmlElement()]
		//[CLSCompliant(false)]
		public Distance PolarRadius
		{
			get
			{
				return pPolarRadius;
			}
		}

		/// <summary>Represents the distance from Earth's center to the equator.</summary>
		/// <value>A <strong>Distance</strong> containing Earth's equatorial radius.</value>
		/// <remarks>
		/// 	<para>This property defines the radius of the Earth from its center to the equator.
		///     This property is used in conjunction with the <strong>SemiMinorAxis</strong>
		///     property to define an ellipsoidal shape. This property returns the same value as
		///     the <strong>EquatorialRadius</strong> property.</para>
		/// </remarks>
		/// <seealso cref="EquatorialRadius">EquatorialRadius Property</seealso>
		//[XmlIgnore()]
		//[CLSCompliant(false)]
		public Distance SemiMajorAxis
		{
			get
			{
				return pEquatorialRadius;
			}
		}

		/// <summary>Represents the distance from Earth's center to the North or South pole.</summary>
		/// <value>A <strong>Distance</strong> containing Earth's polar radius.</value>
		/// <remarks>
		/// 	<para>This property defines the radius of the Earth from its center to the equator.
		///     This property is used in conjunction with the <strong>SemiMajorAxis</strong>
		///     property to define an ellipsoidal shape. This property returns the same value as
		///     the <strong>PolarRadius</strong> property.</para>
		/// </remarks>
		/// <seealso cref="EquatorialRadius">EquatorialRadius Property</seealso>
		//[XmlIgnore()]
		//[CLSCompliant(false)]
		public Distance SemiMinorAxis
		{
			get
			{
				return pPolarRadius;
			}
		}

		/// <summary>Indicates if the ellipsoid is describing a perfect sphere.</summary>
		/// <remarks>
		/// Mathematical formulas such as map projection and coordinate conversion can be
		/// optimized if the ellipsoid they are working with is spherical. For more precise
		/// results, however, spherical ellipsoids should not be used. This property, when used
		/// correctly, can improve performance for mathematics when coordinate precision is less of
		/// a concern, such as viewing a map from a high altitude.
		/// </remarks>
		public bool IsSpherical
		{
			get
			{
				return pEquatorialRadius.Equals(pPolarRadius);
			}
		}

		/// <summary>
		/// Indicates the inverse of the shape of an ellipsoid relative to a sphere.
		/// </summary>
		/// <value>A <strong>Double</strong> containing the ellipsoid's flattening.</value>
		/// <remarks>
		/// This property is used frequently in equations. Inverse flattening is defined as
		/// one divided by the <strong>Flattening</strong> property.:
		/// </remarks>
		/// <seealso cref="EquatorialRadius">EquatorialRadius Property</seealso>
		//[XmlIgnore()]
		public double InverseFlattening
		{
			get
			{
				return pInverseFlattening;
			}
		}

		/// <summary>Indicates the shape of the ellipsoid relative to a sphere.</summary>
		/// <value>A <strong>Double</strong> containing the ellipsoid's flattening.</value>
		/// <remarks>
		/// This property compares the equatorial radius with the polar radius to measure the
		/// amount that the ellipsoid is "squished" vertically.
		/// </remarks>
		/// <seealso cref="EquatorialRadius">EquatorialRadius Property</seealso>
		//[XmlIgnore()]
		public double Flattening
		{
			get
			{
				return pFlattening;
			}
		}

		/// <summary>Returns the rate of flattening of the ellipsoid.</summary>
		/// <value>A <strong>Double</strong> measuring how elongated the ellipsoid is.</value>
		/// <remarks>
		/// The eccentricity is a positive number less than 1, or 0 in the case of a circle.
		/// The greater the eccentricity is, the larger the ratio of the equatorial radius to the
		/// polar radius is, and therefore the more elongated the ellipse is.
		/// </remarks>
		//[XmlIgnore()]
		public double Eccentricity
		{
			get
			{
				return pEccentricity;
			}
		}

		/// <summary>Returns the square of the eccentricity.</summary>
		/// <remarks>
		/// This property returns the value of the <strong>Eccentricity</strong> property,
		/// squared. It is used frequently during coordinate conversion formulas.
		/// </remarks>
		//[XmlIgnore()]
		public double EccentricitySquared
		{
			get
			{
				return pEccentricitySquared;
			}
		}

        /// <summary>
        /// Returns whether the current ellipsoid has the same value as the specified ellipsoid.
        /// </summary>
        /// <param name="value">An <strong>Ellipsoid</strong> object to compare against.</param>
        /// <returns>A <strong>Boolean</strong>, <strong>True</strong> if the equatorial radius and polar radius
        /// of both ellipsoids are equal.  When both radii are equal, all other calculated properties will also 
        /// be equal.  The name of the ellipsoid is not compared.</returns>
		public bool Equals(Ellipsoid value)
		{
            if (value == null)
                return false;

			return value.EquatorialRadius.Equals(pEquatorialRadius)
				&& value.PolarRadius.Equals(pPolarRadius);
		}

		public bool Equals(Ellipsoid value, int decimals)
		{
            if (value == null)
                return false;

			return value.EquatorialRadius.Equals(pEquatorialRadius, decimals)
				&& value.PolarRadius.Equals(pPolarRadius, decimals);
		}

        /// <summary>
        /// Returns a Ellipsoid object matching the specified name.
        /// </summary>
        /// <param name="ellipsoidName">A <strong>String</strong> describing the name of an existing Ellipsoid.</param>
        /// <returns>A <strong>Ellipsoid</strong> object matching the specified string, or null if no Ellipsoid was found.</returns>
        public static Ellipsoid FromName(string name)
        {
            foreach (Ellipsoid item in pEllipsoids)
            {
                if (item.Name == name)
                    return item;
            }
            return null;
        }

		#region IFormattable Members

		public override string ToString()
		{
			return pName;
		}

		string IFormattable.ToString(string format, IFormatProvider formatProvider)
		{
			return ToString();
		}

		#endregion

#if !PocketPC || (PocketPC && Framework20)
		#region IXmlSerializable Members


#if Framework20
        public static XmlQualifiedName GetXmlSchema(XmlSchemaSet xs)
        {
            // Load the schema
            xs.Add(Xml.SchemaSet);

            // And return the type
            return new XmlQualifiedName("Ellipsoid", Xml.Namespace);
        }
#endif

        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            ReadXml(reader);
        }

        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            WriteXml(writer);
        }

        /// <summary>
        /// Populates the current instance by reading XML from the specified reader.
        /// </summary>
        /// <param name="reader"></param>
        public void ReadXml(XmlReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            reader.MoveToAttribute("Name", Xml.Namespace);
            pName = reader.Value;

            // Create a Distance deserializer
            XmlSerializer DistanceSerializer = new XmlSerializer(typeof(Distance));

            // Now read in the elements
            while (!reader.EOF)
            {
                // Keep reading until we reach an element
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        switch (reader.Name)
                        {
                            case "PolarRadius":
                                reader.Read();
                                pPolarRadius = (Distance)DistanceSerializer.Deserialize(reader);
                                break;
                            case "EquatorialRadius":
                                reader.Read();
                                pEquatorialRadius = (Distance)DistanceSerializer.Deserialize(reader);
                                break;
                            default:
                                // Read the next item
                                reader.Read();
                                break;
                        }
                        break;
                    case XmlNodeType.EndElement:
                        if (String.Compare(reader.Name, "Ellipsoid", true, CultureInfo.InvariantCulture) == 0)
                        {
                            // Done reading!  Rebuild the ellipsoid
                            Calculate();
                            return;
                        }
                        else
                        {
                            // Keep going
                            reader.Read();
                            continue;
                        }
                    default:
                        // Read the next item
                        reader.Read();
                        break;
                }
            }
        }

        /// <summary>
        /// Writes the values of the current instance to the specified XML writer.
        /// </summary>
        /// <param name="writer"></param>
        public void WriteXml(XmlWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");
       
            writer.WriteStartAttribute("Name", Xml.Namespace);
            writer.WriteString(pName);
            writer.WriteEndAttribute();
            
            // Defer to the Distance serializer for other stuff
            XmlSerializer x = new XmlSerializer(typeof(Distance));
            writer.WriteStartElement("EquatorialRadius");
            x.Serialize(writer, pEquatorialRadius);
            writer.WriteEndElement();

            writer.WriteStartElement("PolarRadius");
            x.Serialize(writer, pPolarRadius);
            writer.WriteEndElement();

            // Write out calculated values.  These values are not read during de-serialization;
            // they are used only for reference in case the consumer doesn't know how to calculate.
            writer.WriteStartElement("Flattening");
            writer.WriteString(pFlattening.ToString(CultureInfo.InvariantCulture));
            writer.WriteEndElement();
            writer.WriteStartElement("InverseFlattening");
            writer.WriteString(pInverseFlattening.ToString(CultureInfo.InvariantCulture));
            writer.WriteEndElement();
            writer.WriteStartElement("Eccentricity");
            writer.WriteString(pEccentricity.ToString(CultureInfo.InvariantCulture));
            writer.WriteEndElement();
            writer.WriteStartElement("EccentricitySquared");
            writer.WriteString(pEccentricitySquared.ToString(CultureInfo.InvariantCulture));
            writer.WriteEndElement();
        }
        #endregion
#endif

#if !PocketPC || DesignTime
        #region ISerializable Members

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]  
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Name", pName);
            info.AddValue("EquatorialRadius", pEquatorialRadius);
            info.AddValue("PolarRadius", pPolarRadius);
        }

        #endregion
#endif
	}

	#region Old Code
//	/// <summary>Indicates an interpretation of the Earth's shape.</summary>
//	/// <remarks>
//	/// This property is used by the <see cref="Ellipsoid.Type">Type</see> property of the <see cref="Ellipsoid">Ellipsoid</see> class to identify
//	/// this ellipsoid is associated with a positional measurement. Most GPS devices report
//	/// coordinates using the WGS1984 ellipsoid.  The name of an ellipsoid is typically the scientist
//	/// or organization who made the formulation followed by the year of the formulation.
//	/// </remarks>
//	/// <seealso cref="Ellipsoid.Type">Type Property (Ellipsoid Class)</seealso>
//	public enum EllipsoidType: int
//	{
//		/// <summary>Airy 1830</summary>
//		Airy1830,
//		/// <summary>ATS 1977</summary>
//		Ats1977,
//		/// <summary>Australian Natioanl 1965</summary>
//		AustralianNational1965,
//		/// <summary>Bessel 1841</summary>
//		Bessel1841,
//		/// <summary>Bessel 1841 (Japan)</summary>
//		Bessel1841Japan,
//		/// <summary>Bessel 1841 (Namibia)</summary>
//		Bessel1841Namibia,
//		/// <summary>Clarke 1866</summary>
//		Clarke1866,
//		/// <summary>Clarke 1880</summary>
//		Clarke1880,
//		/// <summary>Everest</summary>
//		Everest,
//		/// <summary>Everest India 1830</summary>
//		EverestIndia1830,
//		/// <summary>Everest 1948</summary>
//		Everest1948,
//		/// <summary>Everest 1956</summary>
//		Everest1956,
//		/// <summary>Everest 1969</summary>
//		Everest1969,
//		/// <summary>Everest (Pakistan)</summary>
//		EverestPakistan,
//		/// <summary>Fischer1960</summary>
//		Fischer1960,
//		/// <summary>Fischer 1968</summary>
//		Fischer1968,
//		/// <summary>GRS 80</summary>
//		Grs80,
//		/// <summary>Hayford 1909</summary>
//		Hayford1909,
//		/// <summary>Helmert 1906</summary>
//		Helmert1906,
//		/// <summary>Hough 1960</summary>
//		Hough1960,
//		/// <summary>Indonesian 1974</summary>
//		Indonesian1974,
//		/// <summary>International 1924</summary>
//		International1924,
//		/// <summary>Krassovsky 1940</summary>
//		Krassovsky1940,
//		/// <summary>Mercury 1960</summary>
//		Mercury1960,
//		/// <summary>Airy (Modified)</summary>
//		ModifiedAiry,
//		/// <summary>Fischer 1960 (Modified)</summary>
//		ModifiedFischer1960,
//		/// <summary>Mercury 1968 (Modified)</summary>
//		ModifiedMercury1968,
//		/// <summary>New International 1967</summary>
//		NewInternational1967,
//		/// <summary>A perfect sphere</summary>
//		NormalSphere,
//		/// <summary>Pakistan</summary>
//		Pakistan,
//		/// <summary>SGS 1985</summary>
//		Sgs1985,
//		/// <summary>South American 1969</summary>
//		SouthAmerican1969,
//		/// <summary>Southeast Asia1830</summary>
//		SoutheastAsia,
//		/// <summary>A custom ellipsoid</summary>
//		UserDefined,
//		/// <summary>World Geodetic System 1966</summary>
//		Wgs1960,
//		/// <summary>World Geodetic System 1966</summary>
//		Wgs1966,
//		/// <summary>World Geodetic System 1972</summary>
//		Wgs1972,
//		/// <summary>World Geodetic System 1984</summary>
//		Wgs1984
//	}
	#endregion
}